#include <windows.h>
#include <glf/core/condition.h>
#include <glf/core/mutex.h>
#include <glf/core/atomic.h>
#include <glf/core/spinlock.h>
#include <glf/core/semaphore.h>
#include <limits>

#ifdef max
#undef max
#endif

namespace glf {

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mutex
////////////////////////////////////////////////////////////////////////////////////////////////////
struct Mutex::Impl {
	Impl(MutexType type)
#if GLF_DEBUG
		: mIsLocked(false)
		, mType(type)
#endif
	{
		mHandle = CreateMutex(NULL, FALSE, NULL);
	}

	~Impl() {
		CloseHandle(mHandle);
	}

	void Lock() {
#if GLF_DEBUG
		DWORD waitResult =
#endif
			WaitForSingleObject(mHandle, INFINITE);
#if GLF_DEBUG
		GLF_ASSERT(waitResult == WAIT_OBJECT_0);
		GLF_ASSERT(mType == MUTEX_RECURSIVE || !mIsLocked);
		mIsLocked = true;
#endif
	}

	bool TryLock() {
		DWORD waitResult = WaitForSingleObject(mHandle, 1);
		bool locked = WAIT_OBJECT_0 == waitResult;
#if GLF_DEBUG
		if (locked)
		{
			GLF_ASSERT(mType == MUTEX_RECURSIVE || !mIsLocked);
			mIsLocked = true;
		}
#endif
		return locked;
	}

	void Unlock() {

#if GLF_DEBUG
		GLF_ASSERT(mType == MUTEX_RECURSIVE || mIsLocked);
		mIsLocked = false;
#endif
		ReleaseMutex(mHandle);
	}

	HANDLE		mHandle;
#if GLF_DEBUG
	bool		mIsLocked;
	MutexType	mType;
#endif
};


////////////////////////////////////////////////////////////////////////////////////////////////////
// Condition implementation using Mutex and Event
// There is Condition variable in the windows api but it is not supported on all windows version
// Windows Server 2003 and Windows XP/2000:  Condition variables are not supported.
////////////////////////////////////////////////////////////////////////////////////////////////////
struct Condition::Impl {
	Impl(glf::Mutex &mutex) :
		mMutex(mutex.GetImpl()),
		mWaitCount(0),
		mBroadcast(0) {
		mWaiters = CreateSemaphore(NULL, 0, std::numeric_limits<LONG>::max(), NULL);      // semaphore for those in cond_wait()
		mWaitersDone = CreateEvent(
			NULL,                // default security attributes
			TRUE,               // manual-reset event
			FALSE,               // initial state is nonsignaled
			NULL
		);  // auto-reset event after everyone gets a broadcast
		::InitializeCriticalSection(&mWaitCountLock);
	}

	~Impl()
	{
		CloseHandle(mWaiters);
		CloseHandle(mWaitersDone);
		::DeleteCriticalSection(&mWaitCountLock);
	}

	int Signal() {
		EnterCriticalSection(&mWaitCountLock);

		if (mWaitCount > 0) {
			ReleaseSemaphore(mWaiters, 1, 0);
			--mWaitCount;
		}

		LeaveCriticalSection(&mWaitCountLock);
		return 1;
	}

	int Broadcast() {
		EnterCriticalSection(&mWaitCountLock);
		if (mWaitCount == 0) {
			LeaveCriticalSection(&mWaitCountLock);
			return 0;
		}
		mBroadcast += mWaitCount;
		ReleaseSemaphore(mWaiters, mWaitCount, 0);
		mWaitCount = 0;
		LeaveCriticalSection(&mWaitCountLock);

		// fairness: wait for everyone to move from Waiters to mutex
		WaitForSingleObject(mWaitersDone, INFINITE);
		ResetEvent(mWaitersDone);
		// not under WaitCountLock, but still under mutex
		//mDidBroadcast = false;

		return 1;
	}

	int Wait(int time) {
		EnterCriticalSection(&mWaitCountLock);
		++mWaitCount;
		LeaveCriticalSection(&mWaitCountLock);

#if GLF_DEBUG
		mMutex.mIsLocked = false;
#endif

		DWORD waitResult;
		bool last;

		if(time == 0)
		{
			waitResult = SignalObjectAndWait(mMutex.mHandle, mWaiters, INFINITE, FALSE);
			GLF_ASSERT(waitResult == WAIT_OBJECT_0);

			EnterCriticalSection(&mWaitCountLock);
			if(mBroadcast > 0)
			{
				last = --mBroadcast == 0;
			}
			else
			{
				last = false;
			}
			LeaveCriticalSection(&mWaitCountLock);
		}
		else
		{
			waitResult = SignalObjectAndWait(mMutex.mHandle, mWaiters, time, FALSE);
			GLF_ASSERT(waitResult == WAIT_OBJECT_0 || waitResult == WAIT_TIMEOUT);

			EnterCriticalSection(&mWaitCountLock);
			if(waitResult == WAIT_TIMEOUT)
			{
				waitResult = WaitForSingleObject(mWaiters, 0);
				if(waitResult == WAIT_TIMEOUT)
				{
					--mWaitCount;
				}
				GLF_ASSERT(mBroadcast == 0 || waitResult == WAIT_OBJECT_0);
			}
			if(waitResult == WAIT_OBJECT_0 && mBroadcast > 0)
			{
				last = --mBroadcast == 0;
			}
			else
			{
				last = false;
			}
			LeaveCriticalSection(&mWaitCountLock);
		}

		GLF_ASSERT_ONLY(DWORD errorCode);
		if (last) {
			// Tell broadcaster that all Waiters have awoken
			GLF_ASSERT_ONLY(errorCode =) SignalObjectAndWait(mWaitersDone, mMutex.mHandle, INFINITE, FALSE);
		} else {
			GLF_ASSERT_ONLY(errorCode =) WaitForSingleObject(mMutex.mHandle, INFINITE);
		}
		GLF_ASSERT(errorCode == WAIT_OBJECT_0);

#if GLF_DEBUG
		mMutex.mIsLocked = true;
#endif
		// 1: succeed, 0: failed
		return waitResult == WAIT_OBJECT_0 ? 1 : 0;
	}

protected:
	glf::Mutex::Impl &mMutex;
	HANDLE mWaiters;
    HANDLE mWaitersDone;
    CRITICAL_SECTION mWaitCountLock;
    unsigned int mWaitCount;
    unsigned int mBroadcast;
};

////////////////////////////////////////////////////////////////////////////////////////////////////
// SpinLock
////////////////////////////////////////////////////////////////////////////////////////////////////
struct SpinLock::Impl
{
	Impl()
		: mLock(0)
	{
	}

	~Impl()
	{
	}

	void Lock()
	{
		while(InterlockedCompareExchange(&mLock, 1, 0) != 0)
		{
			glf::Thread::Sleep(0);
		}
	}

	bool TryLock()
	{
		return InterlockedCompareExchange(&mLock, 1, 0) == 0;
	}

	void Unlock()
	{
		InterlockedExchange(&mLock, 0);
	}

private:
	LONG mLock;
};

////////////////////////////////////////////////////////////////////////////////////////////////////
// Semaphore
////////////////////////////////////////////////////////////////////////////////////////////////////

struct Semaphore::Impl {
	Impl(unsigned int initialValue, unsigned int maxValue)
	{
		Handle = CreateSemaphore(NULL, initialValue, maxValue, NULL);
	}

	~Impl() {
		CloseHandle(Handle);
	}

	int Wait()
	{
		DWORD waitResult = WaitForSingleObject(Handle, INFINITE);
#if GLF_DEBUG
		GLF_ASSERT(waitResult == WAIT_OBJECT_0);
#endif
		return ((waitResult == WAIT_OBJECT_0) ? 0 : -1);
	}

	int TryWait() {
		DWORD waitResult = WaitForSingleObject(Handle, 1);
#if GLF_DEBUG
		GLF_ASSERT(waitResult == WAIT_OBJECT_0);
#endif
		return ((waitResult == WAIT_OBJECT_0) ? 0 : -1);
	}

	int Post() {
		BOOL postResult = ReleaseSemaphore(Handle, 1, NULL);
#if GLF_DEBUG
		GLF_ASSERT(postResult == TRUE);
#endif
		return ((postResult == TRUE) ? 0 : -1);
	}

	HANDLE		Handle;
};

} //namespace glf

